package com.xiyuan.smartutils.constants;

/**
 * WebSocket常量类
 * <p>
 * 0x00：标识一个中间数据包
 * 0x01：标识一个text类型数据包
 * 0x02：标识一个binary类型数据包
 * 0x08：标识一个断开连接类型数据包
 * 0x09：标识一个ping类型数据包
 * 0x0A：表示一个pong类型数据包
 * <p>
 * 1000         CLOSE_NORMAL            正常关闭; 无论为何目的而创建, 该链接都已成功完成任务.
 * 1001         CLOSE_GOING_AWAY        终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
 * 1002         CLOSE_PROTOCOL_ERROR    由于协议错误而中断连接.
 * 1003         CLOSE_UNSUPPORTED       由于接收到不允许的数据类型而断开连接 (如仅接收文本数据的终端接收到了二进制数据).
 * 1004                                 保留. 其意义可能会在未来定义.
 * 1005         CLOSE_NO_STATUS         保留.  表示没有收到预期的状态码.
 * 1006         CLOSE_ABNORMAL          保留. 用于期望收到状态码时连接非正常关闭 (也就是说, 没有发送关闭帧).
 * 1007         Unsupported Data        由于收到了格式不符的数据而断开连接 (如文本消息中包含了非 UTF-8 数据).
 * 1008         Policy Violation        由于收到不符合约定的数据而断开连接. 这是一个通用状态码, 用于不适合使用 1003 和 1009 状态码的场景.
 * 1009         CLOSE_TOO_LARGE         由于收到过大的数据帧而断开连接.
 * 1010         Missing Extension       客户端期望服务器商定一个或多个拓展, 但服务器没有处理, 因此客户端断开连接.
 * 1011         Internal Error          客户端由于遇到没有预料的情况阻止其完成请求, 因此服务端断开连接.
 * 1012         Service Restart         服务器由于重启而断开连接. [Ref]
 * 1013         Try Again Later         服务器由于临时原因断开连接, 如服务器过载因此断开一部分客户端连接. [Ref]
 * 1014                                 由 WebSocket 标准保留以便未来使用.
 * 1015         TLS Handshake           保留. 表示连接由于无法完成 TLS 握手而关闭 (例如无法验证服务器证书).
 * 1016–1999                            由 WebSocket 标准保留以便未来使用.
 * 2000–2999                            由 WebSocket 拓展保留使用.
 * 3000–3999                            可以由库或框架使用. 不应由应用使用. 可以在 IANA 注册, 先到先得.
 * 4000–4999                            可以由应用使用.
 *
 * @version v1.0.0 @author lgz 2020-6-29 新建与整理
 */
public interface WebSocketConstants extends CodeConstants {
    /*************************************************************************/
    // 操作类型
    /*************************************************************************/
    public static final Opcode MID          = Opcode.CONTINUATION;
    public static final Opcode TEXT         = Opcode.TEXT;
    public static final Opcode BIN          = Opcode.BINARY;
    public static final Opcode CLOSE        = Opcode.CLOSE;
    public static final Opcode PING         = Opcode.PING;
    public static final Opcode PONG         = Opcode.PONG;
    
    /**
     * The Constant MAX_FRAME_LENGTH_16.
     */
    static final int MAX_FRAME_LENGTH_16    = 0xFFFF;
    
    /**
     * The Constant MAX_FRAME_LENGTH_63.
     */
    static final long MAX_FRAME_LENGTH_63   = 0x7FFFFFFFFFFFFFFFL;
    
    /**
     * The Constant FIN_MASK = 0x80
     */
    static final int FIN_MASK               = 1 << 7;
    
    /**
     * The Constant RSV1_MASK = 0x40
     */
    static final byte RSV1_MASK             = 1 << 6;
    
    /**
     * The Constant RSV2_MASK.
     */
    static final byte RSV2_MASK             = 1 << 5;
    
    /**
     * The Constant RSV3_MASK.
     */
    static final byte RSV3_MASK             = 1 << 4;
    
    /**
     * The Constant OPCODE_MASK.
     */
    static final byte OPCODE_MASK           = 0x0F;
    
    /**
     * The Constant MASK_MASK.
     */
    static final int MASK_MASK              = 1 << 7;
    
    /**
     * The Constant PAYLOAD_LEN_MASK.
     */
    static final int PAYLOAD_LEN_MASK       = 0x7F;
    /*************************************************************************/
    // 长度2&8字节
    /*************************************************************************/
    public static final PayloadLength LEN_SHORT       = PayloadLength.LEN_SHORT;
    public static final PayloadLength LEN_16          = PayloadLength.LEN_16;
    public static final PayloadLength LEN_63          = PayloadLength.LEN_63;
    
    /*************************************************************************/
    // 关闭错误码
    /*************************************************************************/
    
    /**
     * 正常关闭; 无论为何目的而创建,该链接都已成功完成任务.
     */
    public static final int CLOSE_NORMAL            = 1000;
    
    public static final int CLOSE_GOING_AWAY        = 1001;
    public static final int CLOSE_PROTOCOL_ERROR    = 1002;
    public static final int CLOSE_UNSUPPORTED       = 1003;
    public static final int CLOSE_NO_STATUS         = 1005;
    public static final int CLOSE_ABNORMAL          = 1006;
    /**
     * Unsupported Data
     */
    public static final int CLOSE_UNSUPPORTED_DATA  = 1007;
    
    /**
     * Policy Violation
     */
    public static final int CLOSE_POLICY_VIOLATION  = 1008;
    
    public static final int CLOSE_TOO_LARGE         = 1009;
    /**
     * Missing Extension
     */
    public static final int CLOSE_MISSING_EXTENSION = 1010;
    
    /**
     * Internal Error
     */
    public static final int CLOSE_INTERNAL_ERROR    = 1011;
    
    /**
     * Service Restart
     */
    public static final int CLOSE_SERVICE_RESTART   = 1012;
    
    /**
     * Try Again Later
     */
    public static final int CLOSE_TRY_AGAIN_LATER   = 1013;
    
    
    /**
     * The Enum RFC6455 Opcode.
     *
     * @author xiyuan
     */
    public static enum Opcode {
        
        /**
         * The CONTINUATION. packet
         */
        CONTINUATION(0x0),
        /**
         * The Text packet.
         */
        TEXT(0x1),
        /**
         * The BINARY_FRAME.
         */
        BINARY(0x2),
        // 0x3-7 reserved
        /**
         * The CONNECTION_CLOSE.
         */
        CLOSE(0x8),
        /**
         * The PING.
         */
        PING(0x9),
        /**
         * The PONG.
         */
        PONG(0xA);
        // 0xB-F reserved
        
        private final int code;
        
        Opcode(int value) {
            this.code = value;
        }
        
        
        @Override
        public String toString() {
            return this.name();
        }
        
        public int intValue(){
            return code;
        }
        public static Opcode of(int value) {
            switch (value) {
                case 0x0:
                    return CONTINUATION;
                case 0x1:
                    return TEXT;
                case 0x2:
                    return BINARY;
                case 0x8:
                    return CLOSE;
                case 0x9:
                    return PING;
                case 0xA:
                    return PONG;
                default:
                    return null;
            }
        }
        
        public static Opcode of(String value) {
            if (value == null) return null;
            Opcode[] opcodes = Opcode.values();
            for (int i = 0; i < opcodes.length; i++) {
                Opcode opcode = opcodes[i];
                if (value.equals(opcode.code + "") || value.equals(opcode.name())) return opcode;
            }
            return null;
        }
    }
    
    public static enum PayloadLength {
        LEN_SHORT((byte)0x7D,0),
        LEN_16((byte)0x7E,2),
        LEN_63((byte)0x7F,8);
        
        private final byte byteValue;
        /**
         * The offset.
         */
        private final int offset;
        
        PayloadLength(byte payloadType, int offset) {
            this.byteValue = payloadType;
            this.offset = offset;
        }
        
        
        public byte byteValue() {
            return byteValue;
        }
        
        /**
         * Offset.
         *
         * @return the int
         */
        public int offset() {
            return offset;
        }
        
        
        
        /**
         * Value of.
         *
         * @param plt the plt
         * @return the payload length type
         */
        public static PayloadLength of(byte plt) {
            switch (plt) {
                case 0x7E:
                    return LEN_16;
                case 0x7F:
                    return LEN_63;
            }
            if (0 <= plt && plt <= 0x7D) {return LEN_SHORT;}
            return null;
        }
        
        /**
         * Value of.
         *
         * @param payloadLength the payload length
         * @return the payload length type
         */
        public static PayloadLength of(long payloadLength) {
            if (payloadLength <= LEN_SHORT.byteValue()) {return LEN_SHORT;}
            else if (payloadLength <= MAX_FRAME_LENGTH_16) {return LEN_16;}
            else if (payloadLength <= MAX_FRAME_LENGTH_63) {return LEN_63;}
            else {throw new IllegalArgumentException("Overflow payload length. payloadLength: " + payloadLength);}
        }
    }
}
